#pragma once
#include <iostream>
#include <string>
#include <list>
#include <vector>
//#include <deque>
#include <sparsehash/dense_hash_map>
#include "Chromosome.hpp"

namespace synteny {
class SyntenyFinder {
	struct SyntenyStatistics {
		size_t syntenyMinimalLenghtThreshold;
		size_t genesCount;
		size_t genesInSyntenies;
		size_t synteniesCount;
		double coverage;
	};
	struct DiscoverPath {
		enum Direction { HEAD, TAIL };
		Direction discoverDirection;
		std::list<NodesList::iterator> path;
		DiscoverPath(Direction discoverDirection, NodesList::iterator begin);
		DiscoverPath() {}
		Node::Sign getLastGeneOrientation();
		Node::Sign getPathOrientation();
		int getLastGeneNumber();
		NodesList::iterator getLastGene();
		void trim(int gene);
	};
	struct DiscoverPathGroup {
		int gene;
		Node::Sign geneSign;
		std::list<std::list<DiscoverPath>::iterator> paths;
		DiscoverPathGroup(std::list<DiscoverPath>::iterator path);
	};
	struct Cycle {
		int gene;
		std::list<DiscoverPath> shortPaths;
		std::list<DiscoverPath> longPaths;
		Cycle(int gene, std::list<DiscoverPath> shortPaths, std::list<DiscoverPath> longPaths)
		: gene(gene), shortPaths(shortPaths), longPaths(longPaths) {}
		Cycle() {}
		void printPaths();
		void printPaths(std::string title, std::list<DiscoverPath> & paths);
	};
	const std::string delimiter;
	size_t girth;
	size_t syntenyMinimalLenghtThreshold;
	google::dense_hash_map<int,std::list<NodesList::iterator>* > nodePointersHashTable;
public:
	std::vector<Chromosome*> chromosomesPtrsVector;

	SyntenyFinder();
	~SyntenyFinder();
	int loadChromosomes(std::string inputFileName);
	void modifySequence(size_t newGirth, size_t newSyntenyMinimalLenghtThreshold);
	void printModifiedSequences();
	void generateDotFile(std::string fileName);
	void printSyntenyStatistics();
	void getStatistics(SyntenyStatistics & syntenyStatistics, size_t syntenyMinimalLenghtThreshold);
private:
	bool checkForBranch(NodesList::iterator nodeIt);
	bool findCycle(NodesList::iterator nodeIt, Cycle & cycle);
	bool rerouteCycle(Cycle & cycle);
	void removeNode(NodesList::iterator & dIt);
	void addNode(NodesList::iterator & cIt, int gene);
	void changeNodeSign(NodesList::iterator & dIt);
	void generatePathsBegins(std::list<DiscoverPath> & discoverPaths
	,  google::dense_hash_map<int, std::list<std::list<DiscoverPath>::iterator> > & visitedNodes
	, NodesList::iterator nodeIt);
	bool discoverPathsAdjacentGene(std::list<DiscoverPath> & discoverPaths
	, google::dense_hash_map<int, std::list<std::list<DiscoverPath>::iterator> > & visitedNodes
	, Cycle & cycle);
	void classifyPaths(std::list<DiscoverPath> & discoverPaths
	, std::list<DiscoverPathGroup> & discoverPathGroups);
	bool isLoop(Cycle & cycle);
	bool isWrongSigns(Cycle & cycle);
	bool isReversalRearengement(Cycle & cycle);
	bool isGenesLoosing(Cycle & cycle);
	bool isDifferentPaths(Cycle & cycle);
	bool reroutLoop(Cycle & cycle);
	bool changeCycleNodesSigns(Cycle & cycle);
	bool reverse(Cycle & cycle);
	bool addGenes(Cycle & cycle);
	bool removeCycle(Cycle & cycle);
	void makeSignsMatched(NodesList::iterator toChangeIt
	, NodesList::iterator & patternIt
	, DiscoverPath::Direction toChangePathDirection
	, DiscoverPath::Direction patternPathDirection);
	void makeNodesSignsMatched(std::list<NodesList::iterator>::iterator & toChangeIt
	, DiscoverPath::Direction toChangeDirection
	, std::list<NodesList::iterator>::iterator & patternIt
	, DiscoverPath::Direction patternDirection);
	bool canMakeNodesSignsMatched(std::list<NodesList::iterator>::iterator & toChangeIt
	, DiscoverPath::Direction toChangeDirection
	, std::list<NodesList::iterator>::iterator & patternIt
	, DiscoverPath::Direction patternDirection);
	void getPathsBranch(std::list<NodesList::iterator>::iterator & longPathIt
	, std::list<NodesList::iterator>::iterator & shortPathIt
	, Cycle & cycle);
	void getPathsBranch(std::list<NodesList::iterator>::iterator & longPathIt
	, std::list<NodesList::iterator>::iterator & shortPathIt
	, DiscoverPath & longPath, DiscoverPath & shortPath);
	bool pathsSignsIsChangeable(std::list<DiscoverPath> & toChangePaths
	, std::list<DiscoverPath> & pattern);
	void changePathNodesSigns(std::list<DiscoverPath> & toChangePaths
	, std::list<DiscoverPath> & pattern);
	void chromosomeAddGenes(DiscoverPath & toChange, DiscoverPath & pattern);
	void chromosomeAddSingleGene(NodesList::iterator toChangeIt
	, NodesList::iterator & patternIt
	, DiscoverPath::Direction toChangePathDirection
	, DiscoverPath::Direction patternPathDirection);
	bool canAddGenes(DiscoverPath & toChange, DiscoverPath & pattern);
	void deleteGenes(int cycleBeginGene, int cycleEndGene, DiscoverPath discoverPath);
};
}
